﻿using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Linq.Expressions;
using System;
using System.Reflection;

namespace PropertyExpression.TypedDataBinding.Web
{
    //TODO: переименовать
    public static class Util
    {
        public static GenericBinder<TextBox, TData> Text<TData, TDataValue>(
            this GenericBinder<TextBox, TData> textBoxBinder,
            Expression<Func<TData, TDataValue>> dataExpression)
        {
            return textBoxBinder.Add(x => x.Text, dataExpression);
        }

        public static RepeaterBinder<TData2> Repeater<TData1, TData2>(
            this IGroupBinder<TData1> groupBinder, Repeater repeater, 
            Func<TData1, IEnumerable<TData2>> func)
        {
            var result = new RepeaterBinder<TData2> { DataSource = () => func(groupBinder.Data) };
            groupBinder.Add(repeater, result);
            return result;
        }

        //TODO: удалить
        //public static void Add<TData, TControl>(
        //    this IGroupBinder<TData> groupBinder,
        //    object control,
        //    ControlBinder<TControl> controlBinder) where TControl : Control
        //{
        //    groupBinder.Add((TControl)control, controlBinder);
        //}

        public static GenericBinder<TControl, TData> Add<TControl, TData>(
            this IGroupBinder<TData> groupBinder,
            TControl control) where TControl : Control
        {
            GenericBinder<TControl, TData> result = new GenericBinder<TControl, TData>(() => groupBinder.Data);
            groupBinder.Add(control, result);
            return result;
        }

        //TODO: убрать
        public static void IfNotNull(this object value, Action action)
        {
            if (value != null)
            {
                action();
            }
        }

        public static void Consert<TArg, TResult>(TArg arg, out TResult result)
        {
            object objectArg = arg;
            if (objectArg is TResult)
            {
                result = (TResult)objectArg;
            }
            else if (objectArg is IConvertible)
            {
                result = (TResult)((IConvertible)objectArg)
                    .ToType(typeof(TResult), null);
            }
            else
            {
                throw new ApplicationException();
            }
        }

        public static IPropertyDelegate<TValue> Property<TEntity, TValue>(
            this TEntity entity, Expression<Func<TEntity, TValue>> expression)
        {
            return new PropertyDelegate<TEntity, TValue>(entity, expression);
        }

        private class PropertyDelegate<TEntity, TValue> : IPropertyDelegate<TValue>
        {
            private readonly TEntity entity;
            private readonly Expression<Func<TEntity, TValue>> expression;
            public PropertyDelegate(TEntity entity, Expression<Func<TEntity, TValue>> expression)
            {
                this.entity = entity;
                this.expression = expression;
            }

            public TValue Value
            {
                get
                {
                    //TODO: optimize
                    return expression.Compile()(entity);
                }
                set
                {
                    //TODO: optimize
                    ((PropertyInfo)((MemberExpression)expression.Body).Member).SetValue(entity, value, null);
                }
            }
        }

        public static T Action<T>(this T t, Action<T> action)
        {
            action(t);
            return t;
        }
    }

    public interface IPropertyDelegate<TValue>
    {
        TValue Value { get; set; }
    }
}
